/* LEDC (LED Controller) basic example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include "esp_err.h"
#include "driver/ledc.h"
#include "ledc_example.h"


#define LEDC_SPEED_MODE         LEDC_LOW_SPEED_MODE
#define LEDC_DUTY_RES           LEDC_TIMER_10_BIT // Set duty resolution to 13 bits
#define LED_DEFAULT_FREQ        (30000)          // Frequency in Hertz. Set frequency at 4 kHz

#define LEDC_W_TIMER            LEDC_TIMER_0
#define LEDC_W_IO               (7)               // Define the output GPIO
#define LED_COLOR_W_CHANNEL     LEDC_CHANNEL_0
#define LED_W_DEFAULT_DUTY      (512)             // Set duty to 25%. (2 ** 10) * 25% = 256

#define LEDC_Y_TIMER            LEDC_TIMER_1
#define LED_COLOR_Y_IO          (1)                // Define the output GPIO
#define LED_COLOR_Y_CHANNEL     LEDC_CHANNEL_3
#define LED_Y_DEFAULT_DUTY      (512)             // Set duty to 25%. (2 ** 10) * 25% = 256

static const char *TAG = "led_pwm";
//队列句柄
QueueHandle_t led_pwm_queue;

typedef struct
{
    ledc_timer_config_t timer;
    ledc_channel_config_t channel;
}led_pwm_t;


led_pwm_t led_pwm_data[2];

/* Warning:
 * For ESP32, ESP32S2, ESP32S3, ESP32C3, ESP32C2, ESP32C6, ESP32H2, ESP32P4 targets,
 * when LEDC_DUTY_RES selects the maximum duty resolution (i.e. value equal to SOC_LEDC_TIMER_BIT_WIDTH),
 * 100% duty cycle is not reachable (duty cannot be set to (2 ** SOC_LEDC_TIMER_BIT_WIDTH)).
 */

static void example_ledc_init(void)
{
    // Prepare and then apply the LEDC PWM timer configuration
    led_pwm_data[WHITE_LED_INDEX].timer.speed_mode = LEDC_SPEED_MODE;
    led_pwm_data[WHITE_LED_INDEX].timer.duty_resolution = LEDC_DUTY_RES;
    led_pwm_data[WHITE_LED_INDEX].timer.timer_num = LEDC_W_TIMER;
    led_pwm_data[WHITE_LED_INDEX].timer.freq_hz = LED_DEFAULT_FREQ; // Set output frequency at 4 kHz
    led_pwm_data[WHITE_LED_INDEX].timer.clk_cfg = LEDC_AUTO_CLK;
    ESP_ERROR_CHECK(ledc_timer_config(&led_pwm_data[0].timer));

    // Prepare and then apply the LEDC PWM channel configuration
    led_pwm_data[WHITE_LED_INDEX].channel.speed_mode = LEDC_SPEED_MODE;
    led_pwm_data[WHITE_LED_INDEX].channel.channel = LED_COLOR_W_CHANNEL;
    led_pwm_data[WHITE_LED_INDEX].channel.timer_sel = LEDC_W_TIMER;
    led_pwm_data[WHITE_LED_INDEX].channel.intr_type = LEDC_INTR_DISABLE;
    led_pwm_data[WHITE_LED_INDEX].channel.gpio_num = LEDC_W_IO;
    led_pwm_data[WHITE_LED_INDEX].channel.duty = 0; // Set duty to 0%
    led_pwm_data[WHITE_LED_INDEX].channel.hpoint = 0;  
    ESP_ERROR_CHECK(ledc_channel_config(&led_pwm_data[WHITE_LED_INDEX].channel));

    // Prepare and then apply the LEDC PWM timer configuration
    led_pwm_data[YELLOW_LED_INDEX].timer.speed_mode = LEDC_SPEED_MODE,
    led_pwm_data[YELLOW_LED_INDEX].timer.duty_resolution = LEDC_DUTY_RES,
    led_pwm_data[YELLOW_LED_INDEX].timer.timer_num = LEDC_Y_TIMER,
    led_pwm_data[YELLOW_LED_INDEX].timer.freq_hz = LED_DEFAULT_FREQ, // Set output frequency at 4 kHz
    led_pwm_data[YELLOW_LED_INDEX].timer.clk_cfg = LEDC_AUTO_CLK;
    ESP_ERROR_CHECK(ledc_timer_config(&led_pwm_data[YELLOW_LED_INDEX].timer));

    // Prepare and then apply the LEDC PWM channel configuration
    led_pwm_data[YELLOW_LED_INDEX].channel.speed_mode = LEDC_SPEED_MODE,
    led_pwm_data[YELLOW_LED_INDEX].channel.channel = LED_COLOR_Y_CHANNEL,
    led_pwm_data[YELLOW_LED_INDEX].channel.timer_sel = LEDC_Y_TIMER,
    led_pwm_data[YELLOW_LED_INDEX].channel.intr_type = LEDC_INTR_DISABLE,
    led_pwm_data[YELLOW_LED_INDEX].channel.gpio_num = LED_COLOR_Y_IO,
    led_pwm_data[YELLOW_LED_INDEX].channel.duty = 0, // Set duty to 0%
    led_pwm_data[YELLOW_LED_INDEX].channel.hpoint = 0;
    ESP_ERROR_CHECK(ledc_channel_config(&led_pwm_data[YELLOW_LED_INDEX].channel));
}


/*************************************************************
 * 白色灯duty 占空比调节:1%~100%
 * 
**********************************************************/
void white_led_freq_duty_update(uint32_t freq, uint8_t duty)
{
    // int max_duty;
    int period = 1024, cur_freq;

    if(duty > 100){
        printf("%s, duty err[%d] \r\n", __func__, duty);
        return;
    }

    cur_freq = freq;
    if((freq > 100000) || (freq < 20000)){
        cur_freq = 30000;
    }

    if(cur_freq > 78000){
        led_pwm_data[WHITE_LED_INDEX].timer.duty_resolution = LEDC_TIMER_9_BIT;
        period = 512;
    }
    else if(cur_freq > 39000){
        led_pwm_data[WHITE_LED_INDEX].timer.duty_resolution = LEDC_TIMER_10_BIT;
        period = 1024;
    }
    else{
        led_pwm_data[WHITE_LED_INDEX].timer.duty_resolution = LEDC_TIMER_11_BIT;
        period = 2048;
    } 

    ESP_ERROR_CHECK(ledc_timer_config(&led_pwm_data[WHITE_LED_INDEX].timer));
    ESP_ERROR_CHECK(ledc_set_freq(LEDC_SPEED_MODE, led_pwm_data[WHITE_LED_INDEX].timer.timer_num, cur_freq));
    // 占空比 50%
    ESP_ERROR_CHECK(ledc_set_duty(LEDC_SPEED_MODE, LED_COLOR_W_CHANNEL, period*duty/100));
    ESP_ERROR_CHECK(ledc_update_duty(LEDC_SPEED_MODE, LED_COLOR_W_CHANNEL));
}

/*************************************************************
 * 黄色灯duty 占空比调节:1%~100%
 * 
**********************************************************/
void yellow_led_freq_duty_update(uint32_t freq, uint8_t duty)
{
    // int max_duty;
    int period = 1024, cur_freq;

    if(duty > 100){
        printf("%s, duty err[%d] \r\n", __func__, duty);
        return;
    }

    cur_freq = freq;
    if((freq > 100000) || (freq < 20000)){
        cur_freq = 30000;
    }

    if(cur_freq > 78000){
        led_pwm_data[YELLOW_LED_INDEX].timer.duty_resolution = LEDC_TIMER_9_BIT;
        period = 512;
    }
    else if(cur_freq > 39000){
        led_pwm_data[YELLOW_LED_INDEX].timer.duty_resolution = LEDC_TIMER_10_BIT;
        period = 1024;
    }
    else{
        led_pwm_data[YELLOW_LED_INDEX].timer.duty_resolution = LEDC_TIMER_11_BIT;
        period = 2048;
    } 

    ESP_ERROR_CHECK(ledc_timer_config(&led_pwm_data[YELLOW_LED_INDEX].timer));
    ESP_ERROR_CHECK(ledc_set_freq(LEDC_SPEED_MODE, led_pwm_data[YELLOW_LED_INDEX].timer.timer_num, cur_freq));
    // 占空比 50%
    ESP_ERROR_CHECK(ledc_set_duty(LEDC_SPEED_MODE, LED_COLOR_Y_CHANNEL, period*duty/100));
    ESP_ERROR_CHECK(ledc_update_duty(LEDC_SPEED_MODE, LED_COLOR_Y_CHANNEL));
}


/*************************************
 * 测试程序 ,占空比和频率一直变化
**************************************/
void pwm_test_breath(void)
{
    static int freq;
    static int duty;

    freq += 1000;
    duty += 10;
    duty %= 100;

    yellow_led_freq_duty_update(freq%80000 +20000, duty);
    white_led_freq_duty_update(freq%80000 +20000, duty);
}

/*************************************
 * led_pwm_handler_task
**************************************/
static void led_pwm_handler_task(void *arg)
{
    (void) arg; //Unused
    led_pwm_msg_t element_message;
    while (1) {
        /* Waiting for touch element messages */
        if(xQueueReceive(led_pwm_queue, &(element_message), portMAX_DELAY))
		{ 
			ESP_LOGI(TAG, "recv_msg_index[%d] \r\n", (led_pwm_index_e)element_message.index);
            ESP_LOGI(TAG, "recv_msg_freq[%d] \r\n", (led_pwm_index_e)element_message.freq);
            ESP_LOGI(TAG, "recv_msg_duty[%d] \r\n", (led_pwm_index_e)element_message.duty);
			//vTaskDelay(pdMS_TO_TICKS(1000));
            if(YELLOW_LED_INDEX == element_message.index){
                yellow_led_freq_duty_update(element_message.freq, element_message.duty);
            }
            else if(WHITE_LED_INDEX == element_message.index){
                white_led_freq_duty_update(element_message.freq, element_message.duty);
            }
		}
    }
}

/*************************************
 * pwm_test_init
**************************************/
void pwm_test_init(void)
{
    // Set the LEDC peripheral configuration
    example_ledc_init();
    // Set duty to 50%
    ESP_ERROR_CHECK(ledc_set_duty(LEDC_SPEED_MODE, LED_COLOR_W_CHANNEL, LED_W_DEFAULT_DUTY));
    ESP_ERROR_CHECK(ledc_set_duty(LEDC_SPEED_MODE, LED_COLOR_Y_CHANNEL, LED_Y_DEFAULT_DUTY));
    // Update duty to apply the new value
    ESP_ERROR_CHECK(ledc_update_duty(LEDC_SPEED_MODE, LED_COLOR_W_CHANNEL));
    ESP_ERROR_CHECK(ledc_update_duty(LEDC_SPEED_MODE, LED_COLOR_Y_CHANNEL));

    led_pwm_queue = xQueueCreate(10, sizeof(led_pwm_msg_t));
    //创建队列返回已创建队列的句柄
	if(led_pwm_queue == 0) 	
	{ 
        ESP_LOGE(TAG, "failed to create queue1= %p \n", led_pwm_queue);
        return;
	}
    xTaskCreate(&led_pwm_handler_task, "led_pwm_handler_task", 2 * 1024, NULL, 5, NULL);

}
